﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using System.Reflection;
using DotNetExtensions.AspNet.SQLites.Attributes;

namespace DotNetExtensions.AspNet.SQLites
{
    public class TableInfo
    {
        public string Prefix = "$";
        public string TableName { get; private set; }
        public string OriginName { get; private set; }
        public string KeyName { get; private set; }
        public Type TableType { get; private set; }
        public string InsertCommandText { get; private set; }
        public string UpdateCommandText { get; private set; }
        public string UpdateCommandPlainText { get; private set; }
        public string CreateCommandText { get; private set; }
        public string DropCommandText { get; private set; }
        public string DeleteCommandText { get; private set; }

        public List<string> ColumnNames
        {
            get
            {
                return Properties.Where(c => c.Ignore == false).Select(c => c.Name).ToList();
            }
        }

        public List<Property> Properties { get; private set; }
        public TableInfo(Type tableType)
        {
            TableType = tableType;
            Parse(tableType);
        }

        private void Parse(Type type)
        {
            if (type == null) throw new ArgumentNullException(nameof(type));

            var attribute = type.GetCustomAttribute<SQLiteTableAttribute>();
            string tableName = type.Name;
            if (attribute != null && !string.IsNullOrEmpty(attribute.Name))
            {
                tableName = attribute.Name;
            }

            TableName = tableName;
            Properties = new List<Property>();

            foreach (var item in type.GetProperties())
            {
                var property = new Property(item);
                if (property.Ignore) continue;

                if (property.Key) KeyName = property.Name;

                Properties.Add(property);
            }

            InitCreateCommandText();
            InitInsertCommandText();
            InitUpdateCommandPlainText();
            UpdateCommandText = InitUpdateCommandText(Properties);
            InitDropCommandText();
            InitDeleteCommandText();
        }
        
        private void InitDropCommandText()
        {
            DropCommandText = $"DROP TABLE IF EXISTS {TableName}";
        }
        private void InitCreateCommandText()
        {
            StringBuilder sb = new StringBuilder();
            sb.AppendLine($"CREATE TABLE IF NOT EXISTS {TableName} (");

            foreach (var item in Properties)
            {
                string key = "";
                string cName = item.Name;
                string cType = item.ColumnType;

                if (item.Key) key = "PRIMARY KEY";

                string point = "";
                if (item.Name != Properties.Last().Name) point = ",";
                sb.AppendLine($" {cName} {cType} {key} NULL {point}");
            }

            sb.AppendLine(")");

            CreateCommandText = sb.ToString();
        }

        private void InitInsertCommandText()
        {
            InsertCommandText = $"INSERT INTO {TableName} ({string.Join(",", ColumnNames)}) VALUES({string.Join(",", ColumnNames.Select(c => Prefix + c))})";
        }

        private string InitUpdateCommandText(List<Property> properties)
        {
            List<string> columnSets = new List<string>();
            foreach (var item in properties)
            {
                string col = $"{item.Name}={Prefix}{item.Name}";
                columnSets.Add(col);
            }

            if (string.IsNullOrEmpty(KeyName)) return $"UPDATE {TableName} SET {string.Join(",", columnSets)} WHERE 1=0";

            return $"UPDATE {TableName} SET {string.Join(",", columnSets)} WHERE {KeyName}={Prefix}{KeyName}";
        }

        private void InitUpdateCommandPlainText()
        {
            List<string> columnSets = new List<string>();
            foreach (var item in Properties)
            {
                string col = $"{item.Name}={Prefix}{item.Name}";
                columnSets.Add(col);
            }

            UpdateCommandPlainText =  $"UPDATE {TableName} SET {string.Join(",", columnSets)}";
        }
        public string RefleshUpdateCommandText(object obj)
        {
            if (obj == null) return UpdateCommandText;

            var properties = TableType.GetProperties();
            List<Property> hasValProperties = new List<Property>();
            foreach (var item in Properties)
            {
                var prop = properties.FirstOrDefault(c => c.Name == item.OriginName);
                if (prop == null) continue;

                object val = prop.GetValue(obj, null);

                if (val == null || val == DBNull.Value) continue;

                hasValProperties.Add(item);
            }

            if (!hasValProperties.Any(c => c.Key)) throw new Exception("Key value can't be null");

            return InitUpdateCommandText(hasValProperties);
        }

        public string RefleshUpdateCommandText(List<string> propertyNames)
        {
            if (propertyNames == null || propertyNames.Count == 0) return UpdateCommandText;

            var properties = TableType.GetProperties();
            List<Property> hasValProperties = new List<Property>();
            foreach (var item in Properties)
            {
                var prop = propertyNames.FirstOrDefault(c => c == item.OriginName);
                if (prop == null) continue;

                hasValProperties.Add(item);
            }

            if (!hasValProperties.Any(c => c.Key)) throw new Exception("Key value can't be null");

            return InitUpdateCommandText(hasValProperties);
        }

        private void InitDeleteCommandText()
        {
            if (string.IsNullOrEmpty(KeyName))
            {
                DeleteCommandText = $"DELETE FROM {TableName} WHERE 0=1";
            }
            else
            {
                DeleteCommandText = $"DELETE FROM {TableName} WHERE {KeyName}={Prefix}{KeyName}";
            }
        }
    }
}
